//
//  LogUtil.cpp
//  mania
//
//  Created by dolly on 14-4-2.
//
//

#include "LogUtil.h"
#include <ctime>
#include <cstdarg>
#include <cstdio>
#include "dirent.h"
#include "MacroUtil.h"

using namespace std;


mutex LogUtil::mtx;
atomic<LogUtil *> LogUtil::instance(nullptr);

#if ((defined(_DEBUG) || defined(DEBUG)) && !defined(NDEBUG))
atomic<bool> LogUtil::allowDebugLog(true);
atomic<bool> LogUtil::allowTimerLog(true);
#else
atomic<bool> LogUtil::allowDebugLog(false);
atomic<bool> LogUtil::allowTimerLog(false);
#endif

void LogUtil::setAllowDebugLog(bool allow) {
	allowDebugLog = allow;
}

void LogUtil::setAllowTimerLog(bool allow) {
	allowTimerLog = allow;
}


LogUtil::LogUtil() : logFile(nullptr) {
	return;
    timerStart();

#if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
    string logPath =  "/Users/woc2006/Documents/";
#else
	string logPath = "D:/log/";
#endif

    // Make log/ dir if it doesn't exist.
    DIR * logDir = opendir(logPath.c_str());
    if (logDir == nullptr) {
        if (malodyMakeDIR(logPath.c_str()) != 0) {
            return;
        } else if ((logDir = opendir(logPath.c_str())) == nullptr) {
            return;
        }
    }

    // Remove old logs
    const double OLD_TIME = 86400.0 * 7.0;
    time_t t = time(nullptr);
    dirent * ent;
    while ((ent = readdir(logDir)) != nullptr) {
        if (ent->d_type != DT_REG) {
            continue;
        }

		char c[2], d;
		if (sscanf(ent->d_name, "log-%*7[0-9]%*1[0-9]T%*5[0-9]%*1[0-9]%*1[-+]%*3[0-9]%*1[0-9].tx%1[t]%c", c, &d) != 1
			&& sscanf(ent->d_name, "log-%*7[0-9]%*1[0-9]T%*5[0-9]%*1[0-9]Z.tx%1[t]%c", c, &d) != 1)
		{
			continue;
		}

        string path = logPath + ent->d_name;
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
        struct _stat st;
        _wstat(Widen(path).c_str(), &st);
#else
        struct stat st;
        stat(path.c_str(), &st);
#endif
        if (difftime(t, st.st_mtime) > OLD_TIME) {
            mRemoveFile(path.c_str());
        }
    }

    closedir(logDir);
    logDir = nullptr;

	char filename[32];
	ISOTime local;
    extractRealtimeToLocalTime(g_launchTime, local);
    snprintf(filename, arraySize(filename), "log-%.4d%.2d%.2dT%.2d%.2d%.2d%+.4d.log",
        local.year, local.month, local.day, local.hour, local.min, local.sec, local.tzmin + local.tzmin / 60 * 40);
	logFile = new ofstream(Widen(logPath + filename), ios_base::app);
	if (logFile->fail()) {
		delete logFile;
		logFile = nullptr;
		return;
	}


}

LogUtil::~LogUtil(){
	delete logFile;
}

LogUtil* LogUtil::getInstance(){
    LogUtil * p = instance.load(memory_order_consume);
    if(p == nullptr) {
        lock_guard<mutex> lck(mtx);
        p = instance.load(memory_order_relaxed);
        if (p == nullptr) {
            p = new LogUtil;
            instance.store(p, memory_order_release);
        }
    }
    return p;
}

void LogUtil::releaseInstance() {
    lock_guard<mutex> lck(mtx);
    delete instance.exchange(nullptr);
}

void LogUtil::writeLog(const char * line, bool flush) {
    lock_guard<mutex> lck(mtx);
	if (logFile == nullptr)
		return;
	*logFile << line << '\n';
	if (flush) {
		logFile->flush();
	}
}

void LogUtil::vaWriteLog(const char * tag, const char * type, const char * format, va_list ap, bool flush) {
	return;
    MMonotonicClockType now;
    getMonotonicClock(now);
    double t = getMonotonicDiffTime(now, g_launchMonotonicTime);
    const size_t MAX_LOG_LINE = 0x1000;
	// 这个地方不可以new，因为有可能用来记录内存不足……
	char s[MAX_LOG_LINE] = {0};
    snprintf(s, MAX_LOG_LINE, "%.6f [%s] %s: ", t, tag, type);
	// 因为VS的vsnprintf有问题，我们减少一个字符长度
    vsnprintf(s + strlen(s), MAX_LOG_LINE - 1 - strlen(s), format, ap);
    writeLog(s, flush);
}

LogUtil * LogUtil::flush() {
    lock_guard<mutex> lck(mtx);
	if (logFile == nullptr)
		return this;
	logFile->flush();
	return this;
}

LogUtil * LogUtil::logDontFlush(const char * tag, const char * format, ...) {
	va_list ap;
	va_start(ap, format);
    vaWriteLog(tag, "LOG", format, ap, false);
	va_end(ap);
	return this;
}

LogUtil * LogUtil::log(const char * tag, const char * format, ...) {
	va_list ap;
	va_start(ap, format);
    vaWriteLog(tag, "LOG", format, ap, true);
	va_end(ap);
	return this;
}

LogUtil * LogUtil::debug(const char * tag, const char * format, ...) {
	if (!allowDebugLog)
		return this;
	va_list ap;
	va_start(ap, format);
    vaWriteLog(tag, "DEBUG", format, ap, true);
	va_end(ap);
	return this;
}

LogUtil * LogUtil::error(const char * tag, const char * format, ...) {
	va_list ap;
	va_start(ap, format);
    vaWriteLog(tag, "ERROR", format, ap, true);
	va_end(ap);
	return this;
}

LogUtil * LogUtil::timerStart() {
    getMonotonicClock(lastTime);
    return this;
}

LogUtil * LogUtil::timerEnd(const char * tag, const char * msg) {
    MMonotonicClockType now;
    getMonotonicClock(now);
	if (allowTimerLog) {
        double dt = getMonotonicDiffTime(now, lastTime);
        double t = getMonotonicDiffTime(now, g_launchMonotonicTime);
        const size_t MAX_LOG_LINE = 0x1000;
		char s[MAX_LOG_LINE] = {0};
        snprintf(s, MAX_LOG_LINE, "%.6f [%s] TIMER: %s %+.3f", t, tag, msg, dt * 1000.0);
        writeLog(s, false);
    }
    lastTime = now;
	return this;
}
